/*
  author Sylvain Bertrand <digital.ragnarok@gmail.com>
  Protected by GNU Affero GPL v3 with some exceptions.
  See README at root of alga tree.
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/i2c.h>

#include <alga/pixel_fmts.h>
#include <alga/timing.h>
#include <alga/amd/dce4/dce4.h>
#include <alga/amd/dce4/dce4_dev.h>

#include "dce4.h"
#include "hpd.h"
#include "crtc.h"

#include "regs.h"

#define RR32(of) dce->ddev.rr32(dce->ddev.dev, (of))
#define WR32(v, of) dce->ddev.wr32(dce->ddev.dev, (v), (of))

/*
 * The dce irqs are not acked by the rlc (block in charge of irq management).
 * It seems the rlc block does not have xrbm access (the block in charge of
 * register management).
 * Then those irqs must be acked manually by the CPU.
 * This is called in hard interrupt context, it does not lock the dce.
 */
void dce4_irqs_ack(struct dce4 *dce)
{
	u32 disps[CRTCS_N_MAX];
	u32 grphs[CRTCS_N_MAX];
	unsigned i;
	u32 tmp;

	for (i = 0; i < dce->ddev.crtcs_n; ++i)
		disps[i] = RR32(regs_disp_int_status[i]);

	for (i = 0; i < dce->ddev.crtcs_n; ++i)
		grphs[i] = RR32(regs_crtc_grph_int_status[i]);

	/* XXX: obsolete page flipping */
	for (i = 0; i < dce->ddev.crtcs_n; ++i)
		if (grphs[i] & GRPH_PFLIP_INT_OCCURRED)
			WR32(GRPH_PFLIP_INT_CLR, regs_crtc_grph_int_status[i]);

	for (i = 0; i < dce->ddev.crtcs_n; ++i) {
		if (disps[i] & vals_lb_d_vblank_int[i])
			WR32(VBLANK_ACK, regs_crtc_vblank_status[i]);
		if (disps[i] & vals_lb_d_vline_int[i])
			WR32(VLINE_ACK, regs_crtc_vline_status[i]);
	}

	for (i = 0; i < HPDS_N; ++i)
		if (disps[i] & vals_hpd_int[i]) {
			tmp = RR32(regs_hpd_int_ctl[i]);
			tmp |= HPDx_INT_CTL_INT_ACK;
			WR32(tmp, regs_hpd_int_ctl[i]);
		}
}
EXPORT_SYMBOL_GPL(dce4_irqs_ack);

int dce4_irqs_thd(struct dce4 *dce)
{
	int r;
	unsigned i;
	unsigned hpds;
	unsigned long flgs;

	spin_lock_irqsave(&dce->irq.lock, flgs);
	hpds = dce->irq.hpds;
	dce->irq.hpds = 0;
	spin_unlock_irqrestore(&dce->irq.lock, flgs);

	r = 0;
	for (i = 0; i < HPDS_N; ++i)
		if (hpds & BIT(i)) {
			r = hpd_irq(dce, i);
			if (r != 0)
				dev_err(dce->ddev.dev, "dce4: unable to service"
					" hpd%u interrupt request\n", i);
		}
	return r;
}
EXPORT_SYMBOL_GPL(dce4_irqs_thd);
